#include "stdafx.h"
#include "xKernel/IxWnd.h"
#include "View.h"

#define IXVIEW_ATTRIBUTE(name, type)              \
    ATTRIBUTE(name, type, IxView, Set##name)

BEGIN_ATTRIBUTE(CxView)
IXVIEW_ATTRIBUTE(LayoutType,            E_ATTRI_INT)
IXVIEW_ATTRIBUTE(BoxRect,               E_ATTRI_RECT)
IXVIEW_ATTRIBUTE(Size,                  E_ATTRI_SIZE)
IXVIEW_ATTRIBUTE(Position,              E_ATTRI_POINT)
IXVIEW_ATTRIBUTE(Margin,                E_ATTRI_RECT)
IXVIEW_ATTRIBUTE(Padding,               E_ATTRI_RECT)
IXVIEW_ATTRIBUTE(Border,                E_ATTRI_RECT)
IXVIEW_ATTRIBUTE(BorderColor,           E_ATTRI_COLORREF)
IXVIEW_ATTRIBUTE(Alignment,             E_ATTRI_ALIGNMENT)
IXVIEW_ATTRIBUTE(State,                 E_ATTRI_INT)
IXVIEW_ATTRIBUTE(Alpha,                 E_ATTRI_BYTE)
IXVIEW_ATTRIBUTE(Check,                 E_ATTRI_BOOL)
IXVIEW_ATTRIBUTE(Enable,                E_ATTRI_BOOL)
IXVIEW_ATTRIBUTE(ShowState,             E_ATTRI_BOOL)
IXVIEW_ATTRIBUTE(FocusState,            E_ATTRI_BOOL)
IXVIEW_ATTRIBUTE(NormalDraw,            E_ATTRI_IXDRAW)
IXVIEW_ATTRIBUTE(HighlightDraw,         E_ATTRI_IXDRAW)
IXVIEW_ATTRIBUTE(PushDraw,              E_ATTRI_IXDRAW)
IXVIEW_ATTRIBUTE(DisableDraw,           E_ATTRI_IXDRAW)
IXVIEW_ATTRIBUTE(CheckNormalDraw,       E_ATTRI_IXDRAW)
IXVIEW_ATTRIBUTE(CheckHighlightDraw,    E_ATTRI_IXDRAW)
IXVIEW_ATTRIBUTE(CheckPushDraw,         E_ATTRI_IXDRAW)
IXVIEW_ATTRIBUTE(CheckDisableDraw,      E_ATTRI_IXDRAW)
END_ATTRIBUTE


CxView::CxView()
    : m_nLayoutType(ELT_HORIZON)
    , m_nAlignment(E_ALIGNMENT_LEFT | E_ALIGNMENT_TOP)
    , m_nZOrder(0)
    , m_nState(E_STATE_NORMAL | E_STATE_ENABLE | E_STATE_UNCHECK)
    , m_rcBox(0, 0, 0, 0)
    , m_rcMargin(0, 0, 0, 0)
    , m_rcPadding(0, 0, 0, 0)
    , m_rcBorder(0, 0, 0, 0)
    , m_clrBorder(CLR_INVALID)
    , m_byAlpha(0xFF)
    , m_bShow(TRUE)
    , m_bFocus(false)
#if _DEBUG
    , __count(0)
#endif
{
    ATTRUBTES(CxView)
}

CxView::~CxView()
{}

HRESULT CxView::GetLayoutType(LPINT pnType)
{
    if (!pnType)    return E_POINTER;

    *pnType = m_nLayoutType;

    return S_OK;
}

HRESULT CxView::SetLayoutType(INT nType)
{
    m_nLayoutType = nType;
    return S_OK;
}

HRESULT CxView::GetBoxRect(LPRECT lprcBox)
{
    if (!lprcBox)   return E_POINTER;

    *lprcBox = m_rcBox;

    return S_OK;
}

HRESULT CxView::SetBoxRect(RECT rcBox)
{
    if (m_rcBox != rcBox)
    {
        Invalidate(m_rcBox);
        m_rcBox = rcBox;
        SchedulePaint(GetEntity<IxView>());
        Invalidate(m_rcBox);
    }
    return S_OK;
}

HRESULT CxView::GetSize(LPSIZE lpszSize)
{
    if (!lpszSize)  return E_POINTER;

    *lpszSize = m_rcBox.Size();

    return S_OK;
}

HRESULT CxView::SetSize(SIZE szSize)
{
    return _set_box_rect(CRect(m_rcBox.TopLeft(), szSize));
}

HRESULT CxView::GetPosition(LPPOINT pptPos)
{
    if (!pptPos)    return E_POINTER;
    *pptPos = m_rcBox.TopLeft();
    return S_OK;
}

HRESULT CxView::SetPosition(POINT ptPos)
{
    return _set_box_rect(CRect(ptPos, m_rcBox.Size()));
}

HRESULT CxView::GetMargin(LPRECT lprcMargin)
{
    if (!lprcMargin)    return E_POINTER;

    *lprcMargin = m_rcMargin;

    return S_OK;
}

HRESULT CxView::SetMargin(RECT rcMargin)
{
    m_rcMargin = rcMargin;
    return S_OK;
}

HRESULT CxView::GetPadding(LPRECT lprcPadding)
{
    if (!lprcPadding)   return E_POINTER;

    *lprcPadding = m_rcPadding;

    return S_OK;
}

HRESULT CxView::SetPadding(RECT rcPadding)
{
    m_rcPadding = rcPadding;
    return S_OK;
}

HRESULT CxView::GetBorder(LPRECT lprcBorder)
{
    if (!lprcBorder)    return E_POINTER;

    *lprcBorder = m_rcBorder;

    return S_OK;
}

HRESULT CxView::SetBorder(RECT rcBorder)
{
    m_rcBorder = rcBorder;
    return S_OK;
}

HRESULT CxView::GetBorderColor(LPCOLORREF lpclrBorder)
{
    if (!lpclrBorder)
    {
        return E_POINTER;
    }

    *lpclrBorder = m_clrBorder & 0x00FFFFFF;

    return S_OK;
}

HRESULT CxView::SetBorderColor(COLORREF clrBorder)
{
    m_clrBorder = clrBorder | 0xFF000000;
    return S_OK;
}

HRESULT CxView::GetAlignment(LPINT pnAlignment)
{
    if (!pnAlignment)
    {
        return E_POINTER;
    }

    *pnAlignment = m_nAlignment;

    return S_OK;
}

HRESULT CxView::SetAlignment(INT nAlignment)
{
    m_nAlignment = nAlignment;
    return S_OK;
}

HRESULT CxView::GetState(LPINT pnState)
{
    if (!pnState)
    {
        return E_POINTER;
    }

    *pnState = (m_nState & 0x0FFF);

    return S_OK;
}

HRESULT CxView::SetState(INT nState)
{
    INT nNormalState = E_STATE_MASK & nState;
    if (nNormalState)
    {
        if (nNormalState >= E_STATE_COUNT)
        {
            ATLASSERT(!L"set the wrong state!!!");
            return E_FAIL;
        }

        m_nState &= ~E_STATE_MASK;
        m_nState |= nNormalState;
    }

    INT nCheck = E_STATE_CHECK_MASK & nState;
    if (nCheck)
    {
        m_nState &= ~E_STATE_CHECK;
        m_nState |= nCheck;
    }

    INT nEnable = E_STATE_ENABLE_MASK & nState;
    if (nEnable)
    {
        m_nState &= ~E_STATE_ENABLE_MASK;
        m_nState |= nEnable;
    }

    return S_OK;
}

HRESULT CxView::GetAlpha(LPBYTE pbyAlpha)
{
    if (!pbyAlpha)  return E_POINTER;

    *pbyAlpha = m_byAlpha;

    return S_OK;
}

HRESULT CxView::SetAlpha(BYTE byAlpha)
{
    m_byAlpha = byAlpha;
    return S_OK;
}

HRESULT CxView::GetZOrder(LPINT pnZorder)
{
    if (!pnZorder)
    {
        return E_POINTER;
    }

    *pnZorder = m_nZOrder;

    return S_OK;
}

HRESULT CxView::SetZOrder(INT nZorder)
{
    m_nZOrder = nZorder;
    return S_OK;
}

HRESULT CxView::GetCheck(LPBOOL pbCheck)
{
    if (!pbCheck)
    {
        return E_POINTER;
    }

    *pbCheck = _is_checked();

    return S_OK;
}

HRESULT CxView::SetCheck(BOOL bCheck)
{
    return SetState(bCheck ? E_STATE_CHECK : E_STATE_UNCHECK);
}

HRESULT CxView::GetEnable(LPBOOL pbEnable)
{
    if (!pbEnable)
    {
        return E_POINTER;
    }

    *pbEnable = ((m_nState & E_STATE_ENABLE_MASK) == E_STATE_ENABLE);

    return S_OK;
}

HRESULT CxView::SetEnable(BOOL bEnable)
{
    return SetState(bEnable ? E_STATE_ENABLE : E_STATE_DISABLE);
}

HRESULT CxView::GetNormalDraw(IxDraw** ppDraw)
{
    return _get_draw_by_state(E_STATE_NORMAL, false, ppDraw);
}

HRESULT CxView::SetNormalDraw(IxDraw* pDraw)
{
    m_pDraw[E_STATE_NORMAL] = pDraw;
    return S_OK;
}

HRESULT CxView::GetHighlightDraw(IxDraw** ppDraw)
{
    return _get_draw_by_state(E_STATE_HIGHLIGHT, false, ppDraw);
}

HRESULT CxView::SetHighlightDraw(IxDraw* pDraw)
{
    m_pDraw[E_STATE_HIGHLIGHT] = pDraw;
    return S_OK;
}

HRESULT CxView::GetPushDraw(IxDraw** ppDraw)
{
    return _get_draw_by_state(E_STATE_PUSH, false, ppDraw);
}

HRESULT CxView::SetPushDraw(IxDraw* pDraw)
{
    m_pDraw[E_STATE_PUSH] = pDraw;
    return S_OK;
}

HRESULT CxView::GetDisableDraw(IxDraw** ppDraw)
{
    return _get_draw_by_state(E_STATE_COUNT, false, ppDraw);
}

HRESULT CxView::SetDisableDraw(IxDraw* pDraw)
{
    m_pDraw[E_STATE_COUNT] = pDraw;
    return S_OK;
}

HRESULT CxView::GetCheckNormalDraw(IxDraw** ppDraw)
{
    return _get_draw_by_state(E_STATE_NORMAL, true, ppDraw);
}

HRESULT CxView::SetCheckNormalDraw(IxDraw* pDraw)
{
    m_pDrawCheck[E_STATE_NORMAL] = pDraw;
    return S_OK;
}

HRESULT CxView::GetCheckHighlightDraw(IxDraw** ppDraw)
{
    return _get_draw_by_state(E_STATE_PUSH, true, ppDraw);
}

HRESULT CxView::SetCheckHighlightDraw(IxDraw* pDraw)
{
    m_pDrawCheck[E_STATE_HIGHLIGHT] = pDraw;
    return S_OK;
}

HRESULT CxView::GetCheckPushDraw(IxDraw** ppDraw)
{
    return _get_draw_by_state(E_STATE_PUSH, true, ppDraw);
}

HRESULT CxView::SetCheckPushDraw(IxDraw* pDraw)
{
    m_pDrawCheck[E_STATE_PUSH] = pDraw;
    return S_OK;
}

HRESULT CxView::GetCheckDisableDraw(IxDraw** ppDraw)
{
    return _get_draw_by_state(E_STATE_COUNT, true, ppDraw);
}

HRESULT CxView::SetCheckDisableDraw(IxDraw* pDraw)
{
    m_pDrawCheck[E_STATE_COUNT] = pDraw;
    return S_OK;
}

HRESULT CxView::GetShowState(LPBOOL pbShow)
{
    if (!pbShow)    return E_POINTER;
    *pbShow = m_bShow;
    return S_OK;
}

HRESULT CxView::SetShowState(BOOL bShow)
{
    m_bShow = !!bShow;
    return S_OK;
}

HRESULT CxView::GetVisibleState(LPBOOL pbVisible)
{
    if (!pbVisible)
    {
        return E_POINTER;
    }

    *pbVisible = m_bShow;

    if (m_bShow)
    {
        CComQIPtr<IxView> spParentView = m_pParent;
        if (spParentView)
        {
            BOOL bVisible = FALSE;
            spParentView->GetVisibleState(&bVisible);

            *pbVisible = bVisible;
        }
    }

    return S_OK;
}

HRESULT CxView::SetFocusState(BOOL bFocus)
{
    // to do ...
    m_bFocus = !!bFocus;
    return S_OK;
}

HRESULT CxView::GetFocusState(LPBOOL pbFocus)
{
    // to do ...
    if (pbFocus)
    {
        return E_POINTER;
    }

    *pbFocus = m_bFocus;

    return S_OK;
}

HRESULT CxView::GetVisibleRect(LPRECT lprcVisible)
{
    if (!lprcVisible)
    {
        return E_POINTER;
    }

    BOOL bVisible = FALSE;
    GetVisibleState(&bVisible);

    if (!bVisible)
    {
        lprcVisible->left = lprcVisible->right = lprcVisible->top = lprcVisible->bottom = 0;
        return S_OK;
    }

    CComQIPtr<IxView> spParentView = m_pParent;
    if (spParentView)
    {
        CRect rcVisibleParent(0, 0, 0, 0);
        spParentView->GetVisibleRect(&rcVisibleParent);

        rcVisibleParent -= rcVisibleParent.TopLeft();

        CRect rcVisible = m_rcBox;
        rcVisible.IntersectRect(&rcVisible, &rcVisibleParent);

        *lprcVisible = rcVisible;
    }
    else
    {
        *lprcVisible = m_rcBox;
    }

    return S_OK;
}

HRESULT CxView::GetAbsoluteRect(LPRECT lprcAbsolute)
{
    // to do ...
    return S_OK;
}

HRESULT CxView::HitTest(POINT pt, LPBOOL pbIn)
{
    if (!pbIn)
    {
        return E_POINTER;
    }

    *pbIn = FALSE;

    if (m_rcBox.PtInRect(pt))
    {
        // to do .. 
        // mask hit test

        *pbIn = TRUE;
    }

    return S_OK;
}

HRESULT CxView::Paint(IxCanvas* pCanvas)
{
    HRESULT hRet = E_FAIL;

    INT nPaintIndex = _get_paind_index();
    if (!_is_checked())
    {
        if (m_pDraw[nPaintIndex])
        {
            hRet = m_pDraw[nPaintIndex]->Draw(pCanvas, m_rcBox);
        }
        else if (m_pDraw[E_STATE_NORMAL])
        {
            hRet = m_pDraw[E_STATE_NORMAL]->Draw(pCanvas, m_rcBox);
        }
        else
        {
            ATLASSERT(!L"Didn't find the draw object");
        }
    }
    else
    {
        if (m_pDrawCheck[nPaintIndex])
        {
            hRet = m_pDrawCheck[nPaintIndex]->Draw(pCanvas, m_rcBox);
        }
        else if (m_pDrawCheck[E_STATE_NORMAL])
        {
            hRet = m_pDrawCheck[E_STATE_NORMAL]->Draw(pCanvas, m_rcBox);
        }
        else
        {
            ATLASSERT(!L"Didn't find the draw object");
        }
    }

    return hRet;
}

HRESULT CxView::BorderPaint(IxCanvas* pCanvas)
{
    if (!pCanvas)
    {
        return E_FAIL;
    }

    if (m_rcBorder.IsRectNull())
    {
        return S_FALSE;
    }

    CRect rc(m_rcBox);
    rc.bottom = rc.top + m_rcBorder.top;
    pCanvas->FillSolidRect(rc, m_clrBorder);

    rc = m_rcBox;
    rc.top = rc.bottom - m_rcBorder.bottom;
    pCanvas->FillSolidRect(rc, m_clrBorder);

    rc = m_rcBox;
    rc.right = rc.left + m_rcBorder.left;
    pCanvas->FillSolidRect(rc, m_clrBorder);

    rc = m_rcBox;
    rc.left = rc.right - m_rcBorder.right;
    pCanvas->FillSolidRect(rc, m_clrBorder);

    return S_OK;
}

HRESULT CxView::FocusPaint(IxCanvas* pCanvas)
{
    if (!pCanvas)
    {
        return E_FAIL;
    }

    if (!m_bFocus)
    {
        return S_FALSE;
    }

    pCanvas->DrawRetangle(CRect(m_rcBox.left + 2, m_rcBox.top + 2, m_rcBox.right - 2, m_rcBox.bottom - 2));

    return S_OK;
}

HRESULT CxView::OnMessage(LPEVENTPARAM params, LPBOOL pbHandled)
{
    if (!params || !pbHandled)
    {
        return E_POINTER;
    }

    if (XM_PARENT_CHANGE == params->message)
    {
        CComQIPtr<IxSchedule> spSchedule = m_pParent;
        if (spSchedule)
        {
            spSchedule->ScheduleLayout(CComQIPtr<IxView>(m_pParent));
            spSchedule->SchedulePaint(GetEntity<IxView>());
        }
    }

    return __super::OnMessage(params, pbHandled);
}

HRESULT CxView::ScheduleLayout(IxView* pView)
{
    if (m_pParent)
    {
        CComQIPtr<IxSchedule> spSchedule = m_pParent;
        if (spSchedule)
        {
            return spSchedule->ScheduleLayout(pView);
        }
    }
    return E_FAIL;
}

HRESULT CxView::SchedulePaint(IxView* pView)
{
    if (m_pParent)
    {
        CComQIPtr<IxSchedule> spSchedule = m_pParent;
        if (spSchedule)
        {
            return spSchedule->SchedulePaint(pView);
        }
    }
    return E_FAIL;
}

HRESULT CxView::Invalidate(RECT rcDirty)
{
    if (m_pParent)
    {
        CComQIPtr<IxSchedule> spSchedule = m_pParent;
        if (spSchedule)
        {
            return spSchedule->Invalidate(rcDirty);
        }
    }
    return E_FAIL;
}

HRESULT CxView::_get_draw_by_state(INT nState, bool bCheck, IxDraw** ppDraw)
{
    if (!ppDraw)
    {
        return E_POINTER;
    }

    *ppDraw = NULL;

    if (!bCheck)
    {
        if (nState <= E_STATE_CHECK && m_pDraw[nState])
        {
            return m_pDraw[nState]->QueryInterface(__uuidof(IxDraw), (void**)ppDraw);
        }
    }
    else
    {
        if (nState <= E_STATE_CHECK && m_pDrawCheck[nState])
        {
            return m_pDrawCheck[nState]->QueryInterface(__uuidof(IxDraw), (void**)ppDraw);
        }
    }
    return E_FAIL;
}

HRESULT CxView::_set_box_rect(RECT& rcBox)
{
    CComPtr<IxView> spView = GetEntity<IxView>();
    if (spView)
        return spView->SetBoxRect(rcBox);
    else
        return E_FAIL;
}